package main

import (
	"bufio"
	"net"
	"sync"

	log "github.com/Sirupsen/logrus"
)

type Connection struct {
	conn    *net.TCPConn
	reader  *bufio.Reader
	writer  *bufio.Writer
	bufChan chan *Message

	m sync.Mutex
}

func NewConnection(addr string) (*Connection, error) {
	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return nil, err
	}
	c, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return nil, err
	}
	log.Infof("%s connect to %s", c.LocalAddr(), c.RemoteAddr())
	err = c.SetKeepAlive(true)
	if err != nil {
		return nil, err
	}
	conn := Connection{
		conn:    c,
		reader:  bufio.NewReader(c),
		writer:  bufio.NewWriter(c),
		bufChan: make(chan *Message, 10),
	}
	go conn.writeLoop()
	return &conn, nil
}

func (this *Connection) WriteMessage(msg *Message) error {
	//log.Debugf("write messge: type %d, %+v", msg.Type, msg.Body)
	this.bufChan <- msg
	return nil
}

func (this *Connection) FlushMessage(msg *Message) error {
	//log.Debugf("flush message: type %d, %+v", msg.Type, msg.Body)
	this.m.Lock()
	defer this.m.Unlock()
	err := EncodeMessage(msg, this.writer)
	if err != nil {
		return err
	}
	return this.writer.Flush()
}

func (this *Connection) writeLoop() {
	for {
		select {
		case msg, ok := <-this.bufChan:
			//chan关闭
			if !ok {
				return
			}
			this.m.Lock()
			err := EncodeMessage(msg, this.writer)
			if err != nil {
				log.Errorf("encode message: %s", err.Error())
			}
			err = this.writer.Flush()
			if err != nil {
				log.Errorf("flush message %d: %s", msg.Type, err.Error())
			}
			this.m.Unlock()
		}
	}
}

func (this *Connection) ReadMessage() (*Message, error) {
	return DecodeMessage(this.reader)
}

func (this *Connection) Close() error {
	log.Infof("%s close connection %s", this.conn.LocalAddr(), this.conn.RemoteAddr())
	return this.conn.Close()
}
